import org.junit.Assert;
import org.junit.Test;

public class 测试解释器 {

    private static String 执行(String 字节码) {
        var 汇编器 = 字节码汇编器.汇编(字节码);
        var 解释器 = new 解释器(true);
        解释器.加载(汇编器.机器码, 汇编器.取机器码尺寸(), 汇编器.常量池.toArray(), 汇编器.入口函数, new Object[汇编器.取数据尺寸()]);
        解释器.执行();
        return 解释器.输出缓存.toString();
    }


    @Test
    public void 测试指令PRINT() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                sconst "Hello, world!"
                print
                """;

        Assert.assertEquals(执行(字节码), "Hello, world!");
    }

    @Test
    public void 测试指令IADD() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 1
                iconst 2
                iadd
                print
                """;

        Assert.assertEquals(执行(字节码), "3");
    }

    @Test
    public void 测试指令ISUB() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 1
                iconst 2
                isub
                print
                """;

        Assert.assertEquals(执行(字节码), "-1");
    }

    @Test
    public void 测试指令ISUB1() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 2
                iconst 1
                isub
                print
                """;

        Assert.assertEquals(执行(字节码), "1");
    }


    @Test
    public void 测试指令IMUL() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 2
                iconst 3
                imul
                print
                """;

        Assert.assertEquals(执行(字节码), "6");
    }

    @Test
    public void 测试指令ILT() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 1
                iconst 2
                ilt
                print
                """;

        Assert.assertEquals(执行(字节码), "true");
    }

    @Test
    public void 测试指令ILT1() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 2
                iconst 1
                ilt
                print
                """;

        Assert.assertEquals(执行(字节码), "false");
    }

    @Test
    public void 测试指令IEQ() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 1
                iconst 1
                ieq
                print
                """;

        Assert.assertEquals(执行(字节码), "true");
    }

    @Test
    public void 测试指令IEQ1() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 1
                iconst 2
                ieq
                print
                """;

        Assert.assertEquals(执行(字节码), "false");
    }

    @Test
    public void 测试指令FADD() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 1.5
                fconst 0.2
                fadd
                print
                """;

        Assert.assertEquals(执行(字节码), "1.7");
    }

    @Test
    public void 测试指令FSUB() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 1.5
                fconst 0.2
                fsub
                print
                """;

        Assert.assertEquals(执行(字节码), "1.3");
    }

    @Test
    public void 测试指令FMUL() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 2.4
                fconst 0.5
                fmul
                print
                """;

        Assert.assertEquals(执行(字节码), "1.2");
    }

    @Test
    public void 测试指令FLT() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 2.4
                fconst 0.5
                flt
                print
                """;

        Assert.assertEquals(执行(字节码), "false");
    }

    @Test
    public void 测试指令FLT1() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 0.5
                fconst 2.4
                flt
                print
                """;

        Assert.assertEquals(执行(字节码), "true");
    }

    @Test
    public void 测试指令FEQ() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 0.5
                fconst 0.5
                feq
                print
                """;

        Assert.assertEquals(执行(字节码), "true");
    }

    @Test
    public void 测试指令FEQ1() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                fconst 0.5
                fconst 0.51
                feq
                print
                """;

        Assert.assertEquals(执行(字节码), "false");
    }

    @Test
    public void 测试指令ITOF() {
        var 字节码 = """
                .globals 0
                .def main: args=0, locals=0
                iconst 3
                itof
                fconst 0.5
                fmul
                print
                """;

        Assert.assertEquals(执行(字节码), "1.5");
    }


    @Test
    public void 测试指令CALL() {
        var 字节码 = """
                .globals 0
                                
                .def addOne: args=1, locals=0
                load 0      ; 从参数0中取出数值并入栈
                iconst 1
                iadd
                ret
                                
                .def main: args=0, locals=0
                iconst 2
                call addOne()
                print
                """;

        Assert.assertEquals(执行(字节码), "3");
    }

    @Test
    public void 测试指令BR() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    br myAddr       ; 前向引用
                    iconst 1
                myAddr:
                    iconst 2
                    print
                    """;

        Assert.assertEquals(执行(字节码), "2");
    }

    @Test
    public void 测试指令BR1() {
        var 字节码 = """
                    .globals 0
                myAddr:
                    print
                    halt
                    
                    .def main: args=0, locals=0
                    iconst 1
                    br myAddr   ; 非前向引用
                    """;

        Assert.assertEquals(执行(字节码), "1");
    }

    @Test
    public void 测试指令BRT() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    iconst 1
                    iconst 2
                    ilt         ; 返回 true
                    brt trueBranch
                    iconst 1
                    print
                    halt
                trueBranch:
                    iconst 2
                    print
                    """;

        Assert.assertEquals(执行(字节码), "2");
    }

    @Test
    public void 测试指令BRF() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    iconst 1
                    iconst 2
                    ilt         ; 返回 true
                    brf falseBranch
                    iconst 1
                    print
                    halt
                falseBranch:
                    iconst 2
                    print
                    """;

        Assert.assertEquals(执行(字节码), "1");
    }

    @Test
    public void 测试指令CCONST() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    cconst 'a'
                    print
                    """;

        Assert.assertEquals(执行(字节码), "a");
    }

    @Test
    public void 测试指令ICONST() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    iconst 123
                    print
                    """;

        Assert.assertEquals(执行(字节码), "123");
    }

    @Test
    public void 测试指令FCONST() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    fconst 3.14
                    print
                    """;

        Assert.assertEquals(执行(字节码), "3.14");
    }

    @Test
    public void 测试指令SCONST() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    sconst "Hello, world!"
                    print
                    """;

        Assert.assertEquals(执行(字节码), "Hello, world!");
    }

    @Test
    public void 测试指令LOAD() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=0
                    iconst 1
                    iconst 2
                    call addTwo()
                    print
                    halt
                    
                    .def addTwo: args=2, locals=0
                    load 0
                    load 1
                    iadd
                    ret
                    
                    """;

        Assert.assertEquals(执行(字节码), "3");
    }

    @Test
    public void 测试指令GLOAD() {
        var 字节码 = """
                    .globals 2
                    .def main: args=0, locals=0
                    iconst 1
                    gstore 0
                    iconst 2
                    gstore 1
                    call addTwo()
                    print
                    halt
                    
                    .def addTwo: args=0, locals=0
                    gload 0
                    gload 1
                    iadd
                    ret
                    
                    """;

        Assert.assertEquals(执行(字节码), "3");
    }


    @Test
    public void 测试指令STRUCT() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=1
                    struct 2
                    store 0     ; locals[0] = struct
                    
                    iconst 1
                    load 0
                    fstore 0    ; struct[0] = 1
                    
                    iconst 2
                    load 0
                    fstore 1    ; struct[1] = 2
                    
                    load 0
                    fload 0     ; push(struct[0])
                    
                    load 0
                    fload 1     ; push(struct[1])
                    iadd
                    
                    print
                    """;

        Assert.assertEquals(执行(字节码), "3");
    }


    @Test
    public void 测试指令NULL() {
        var 字节码 = """
                    .globals 0
                    .def main: args=0, locals=1
                    
                    iconst 1
                    null
                    null
                    
                    pop
                    pop
                    
                    print
                    """;

        Assert.assertEquals(执行(字节码), "1");
    }
}
